# 首先从Excel读取OTA的HEX包信息
# 然后根据HEX包先生成对应的bin文件
# 根据从Excel读取的信息生成id,seg文件
# 将bin文件和id,seg文件打包成ota文件,文件名由Excel指定
import json
import os
import binascii
import time
import pandas as pd
import glob

def parse_hex_line(line):
    if line.startswith(':02'):
        raise ValueError("Page label, can be ignored")
    if not line.startswith(':'):
        raise ValueError("Line does not start with ':'")

    if not all(c in '0123456789ABCDEF' for c in line[2:-2]):
        raise ValueError("Line contains non-hex characters")

    # Remove the colon and split the line into bytes
    line = line[1:]
    data_bytes = binascii.unhexlify(line[8:-2])  # Exclude the checksum
    return data_bytes

def hex_to_bin(hex_file_path, bin_file_path):
    bin_data = b''
    with open(hex_file_path, 'r') as hex_file:
        for line in hex_file:
            line = line.strip()
            if line:  # Ignore empty lines
                try:
                    data = parse_hex_line(line)
                    bin_data += data
                except ValueError as e:
                    print(f"Error parsing line: {e}")
                    continue
    with open(bin_file_path, 'wb') as bin_file:
        bin_file.write(bin_data)


def get_para_from_xls(excel_path):
    # 读取Excel文件
    sheet_name = 'ota包信息表'
    df = pd.read_excel(excel_path, sheet_name=sheet_name)
    data_json = df.to_json(orient='records', force_ascii=False)
    return data_json

def crc8_maxim(data, offset, length):
    crc = 0  # Initial value
    length += offset
    for j in range(offset, length):
        crc ^= data[j] & 0xFF
        for i in range(8):
            if (crc & 1) == 0:
                crc = (crc >> 1) & 0xFF
            else:
                crc = ((crc >> 1) & 0xFF) ^ 0x8C
    return crc

def parse_Ids(file_path):
    params = {}
    hardware_ids = []

    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            line = line.strip()
            if line and not line.startswith(('#', '/*')):
                key, value = line.split(':', 1)
                key = key.strip()
                value = value.strip()

                if key == 'hardware_id':
                    hardware_ids = value.split(',')
                    params[key] = [hid.strip() for hid in hardware_ids]
                else:
                    params[key] = value

    # 处理注释行，提取注释内容
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            line = line.strip()
            if line.startswith('/*'):
                params['reserve_comment'] = line[2:-2].strip()
    return params
def parse_segments(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        bindary_data = bytearray()
        for line in file:
            line = line.strip()
            if line and not line.startswith(('#', '/*')):
                strArr = line.split(';')
                segments = [segment.strip() for segment in strArr]
                for segment in segments[1:]:
                    if segment.startswith('#') or segment=='':
                        continue
                    if segment.startswith('0x') or segment.startswith('0X'):
                        value = segment[2:]
                        value = int(value, 16)
                        bindary_data.extend([value & 0xFF, (value >> 8) & 0xFF,(value >> 16) & 0xFF,(value >> 24) & 0xFF])
                    else:
                        value = int(segment)
                        bindary_data.extend([value & 0xFF, (value >> 8) & 0xFF,(value >> 16) & 0xFF,(value >> 24) & 0xFF])
                        bindary_data.extend([0x00, 0x00, 0x00, 0x00])
    return bindary_data

def parse_parameters(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        binary_data = bytearray()
        for line in file:
            line = line.strip()
            if line and not line.startswith(('#', '/*')):
                strArr= line.split(';')
                parts = [part.strip() for part in strArr]
                for part in parts[1:]:
                    if part.startswith('#') or part=='':
                        continue
                    if part.startswith('0x') or part.startswith('0X'):
                        value = part[2:]
                        if(len(value)==8):
                            value = (int(value, 16) >> 16) & 0xFFFF
                            binary_data.extend([value & 0xFF, (value >> 8) & 0xFF])
                        elif len(value)==4:
                            value = int(value, 16)
                            binary_data.extend([value & 0xFF, (value >> 8) & 0xFF])
                        else:
                            raise ValueError(f"Invalid hexadecimal value: {value}")
                    else:
                        value = int(part) & 0xFFFF
                        binary_data.extend([value & 0xFF, (value >> 8) & 0xFF])
        return binary_data

def create_ota_file(id_dict, para_file_path,segment_file_path, hex_file_path, bin_file_path,ota_file_path):
    hex_to_bin(hex_file_path, bin_file_path)
    with open(bin_file_path, 'rb') as f:
        bin_data = f.read()

    length_bytes = (len(bin_data)+0x500+1).to_bytes(4, byteorder='little')


    can_id = id_dict['can_id']
    can_id= can_id[2:] # 去掉0x
    can_id_bytes =can_id.encode('ascii')+b'\x00\x00'


    hardware_id_bytes = b''
    hardware_ids = id_dict['hardware_id']

    for i in range(10):
        if i < len(hardware_ids):
            hardware_id_bytes += hardware_ids[i].encode('ascii') + b'\x00' * (15 - len(hardware_ids[i]))
        else:
            hardware_id_bytes += b'\x00' * 15
    if('part_number' in id_dict):
        partnumber = id_dict['part_number']
        partnumber_bytes = partnumber.encode('ascii') + b'\x00' * (37 - len(partnumber))
    else:
        partnumber_bytes = b'\x00' * 37

    version_bytes = b'\x17\x00\x00\x00'
    reserve = id_dict['reserve']
    reserve_bytes = reserve.encode('ascii') + b'\x00' * (0x100-0xC8 - len(reserve))
    if(seg_file_path):
        segments = parse_segments(segment_file_path)
        segments_bytes = bytes(segments) + b'\x00' * (0x300-0x100 - len(segments))
    else:
        segments_bytes = b'\x00' * (0x300-0x100)
    if(par_file_path):
        parameters = parse_parameters(para_file_path)
        parameters_bytes = bytes(parameters) + b'\x00' * (0x500-0x300 - len(parameters))
    else:
        parameters_bytes = b'\x00' * (0x500-0x300)

    header = length_bytes +can_id_bytes+ hardware_id_bytes + partnumber_bytes + version_bytes + reserve_bytes + segments_bytes+ parameters_bytes
    new_bin_data = header + bin_data

    checksum_value = crc8_maxim(new_bin_data,0, len(new_bin_data))
    new_bin_data += checksum_value.to_bytes(1, byteorder='little')
    with open(ota_file_path, 'wb') as f:
        f.write(new_bin_data)

    print(f"New OTA file {ota_file_path} has been created with header of length {len(header)} bytes.")


def compare_binary_files(dierctory,file1, file2):
    # 收集差异点信息
    differences = []
    with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
        while True:
            byte1 = f1.read(1)
            byte2 = f2.read(1)
            if byte1 != byte2:
                # 保存差异点信息
                differences.append({
                    'position': f1.tell() - 1,
                    'position_hex': hex(f1.tell() - 1),
                    'file1_value': byte1.hex().upper(),
                    'file2_value': byte2.hex().upper()
                })
            if not byte1 or not byte2:
                break


    # 确定CSV文件的保存路径

    csv_file_path = os.path.join(dierctory, 'differences.csv')
    if(os.path.exists(csv_file_path)):
        os.remove(csv_file_path)

    # 写入CSV文件
    with open(csv_file_path, 'w', newline='') as csvfile:
        header = ['different position (decimal)', 'different position (hex)', 'file1 value (hex)', 'file2 value (hex)']
        csvfile.write(','.join(header) + '\n')
        for diff in differences:
            row = [
                str(diff['position']),
                diff['position_hex'],
                diff['file1_value'],
                diff['file2_value']
            ]
            csvfile.write(','.join(row) + '\n')

    print(f"Differences saved to {csv_file_path}")

def find_files(directory, extension):
    # 构建搜索模式，例如：'/path/to/directory/*.txt'
    pattern = os.path.join(directory, f'*.{extension}')
    # 使用glob.glob搜索匹配的文件
    files = glob.glob(pattern)
    return files

if __name__ == '__main__':

    hex_to_bin('kgcb_bms_V203_6.hex',"kgcb_bms_V203_6.bin")
    pass
    # 数值为文件夹名称范围，因为下载下来的数据是按ID数字排列的，所以直接根据实际ID范围进行修改
    for i in range(487,488):
        try:
            directory_path = r'C:\Users\A0080437\Downloads\OTA File List\{i}'.format(i=i)
            extension_hex = 'hex'
            hex_file_paths = find_files(directory_path, extension_hex)
            if(len(hex_file_paths) > 0):
                hex_file_path = hex_file_paths[0]
            extensin_id = 'id'
            id_file_paths = find_files(directory_path, extensin_id)
            if(len(id_file_paths) > 0):
                id_file_path = id_file_paths[0]
            extension_ota = 'ota'
            ota_file_paths = find_files(directory_path, extension_ota)
            if(len(ota_file_paths) > 0):
                ota_file_path = ota_file_paths[0]
            extension_par = 'par'
            par_file_paths = find_files(directory_path, extension_par)
            if(len(par_file_paths) > 0):
                par_file_path = par_file_paths[0]
            else:
                par_file_path = None
            extension_seg = 'seg'
            seg_file_paths = find_files(directory_path, extension_seg)
            if(len(seg_file_paths) > 0):
                seg_file_path = seg_file_paths[0]
            else:
                seg_file_path = None
            id_dict = parse_Ids(id_file_path)
            create_ota_file(id_dict,par_file_path,seg_file_path,hex_file_path,hex_file_path.replace('.hex','.bin'),hex_file_path.replace('.hex','.ota'))

        except Exception as e:
            continue
    # 数值为文件夹名称范围，因为下载下来的数据是按ID数字排列的，所以直接根据实际ID范围进行修改
    for i in range(487,488):
        try:
            # 此处为文件夹路径，直接根据实际路径进行修改
            directory_path = r'C:\Users\A0080437\Downloads\OTA File List\{i}'.format(i=i)
            target_files = find_files(directory_path, 'ota')
            if(len(target_files) > 1):
                ota_file_path1 = target_files[0]
                ota_file_path2 = target_files[1]
                compare_binary_files(directory_path, ota_file_path1,ota_file_path2)

        except:
            continue

